///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// global
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var global = (function () {
    return this;
})();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// generic getIdsByType
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * Returns an array of object id's of property _type_ matches the specified type
 * @param type
 * @returns {Array}
 *
 * TODO: move this into utils
 */
function getIdsByType(type) {
    var result = [];
    for (var id in global) {
        if (global.hasOwnProperty(id)) {
            if (global[id] && global[id]._type_ == type) {
                result.push(id);
            }
        }
    }
    return result;
}

function help(obj) {
    if (obj.help) {
        for (var index = 0; index < obj.help.length; index++) {
            console.log(obj.help[index]);
        }
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// feature
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var feature = (function () {

    var loaded = [];

    function create(closure) {
        var result = closure();
        result._type_ = "feature";
        return result;
    }

    create.help = [
        "create(closure)",
        "creates a feature over the specified closure"
    ];

    function load(/* arguments */) {
        for (var index = 0; index < arguments.length; index++) {
            var name = arguments[index];
            if (loaded.indexOf(name) == -1) {
                Feature_load(name);
                loaded.push(name);
            }
        }
    }

    load.help = [
        "load(feature [, feature]*)",
        "loads the specified feature(s) given by their name"
    ];

    function list() {
        return getIdsByType("feature");
    }

    load.help = [
        "list()",
        "returns an array of created/loaded features"
    ];

    return {
        create: create,
        load: load,
        list: list
    }
})();
feature._type_ = "builtin";
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// utils
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var utils = feature.create(function () {
    function mergeObject(a, b) {
        var res = copyObject(a);
        for (var id in b) {
            if (b.hasOwnProperty(id)) {
                if (!a.hasOwnProperty(id)) throw new Error("merge overflow: " + id);
                if (b[id]) {
                    if (b[id] instanceof Object) mergeObject(a[id], b[id]);
                    else res[id] = b[id];
                }
            }
        }
        return res;
    }

    mergeObject.help = [
        "mergeObject(a, b)",
        "returns a merged object of a & b"
    ];

    function copyObject(obj) {
        var res = {};
        for (var id in obj) {
            if (obj.hasOwnProperty(id)) {
                if (res[id] instanceof Object) res[id] = copyObject(obj[id]);
                else res[id] = obj[id];
            }
        }
        return res;
    }

    copyObject.help = [
        "copyObject(obj)",
        "returns a deep copy of an object"
    ];

    return {
        mergeObject: mergeObject,
        copyObject: copyObject
    };
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// console
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var console = feature.create(function () {
    function print(/* arguments */) {
        if (global.name) {
            if (global.currentTask) {
                Console_print.apply(this, [
                    arguments[0], [
                        "[", global.name, "]", "[", global.currentTask, "]"
                    ].concat(Array.prototype.slice.call(arguments, 1)).join(" ")
                ]);
            }
            else {
                Console_print.apply(this, [
                    arguments[0], [
                        "[", global.name, "]"
                    ].concat(Array.prototype.slice.call(arguments, 1)).join(" ")
                ]);
            }
        } else {
            Console_print.apply(this, [
                arguments[0], Array.prototype.slice.call(arguments, 1).join(" ")
            ]);
        }
    }

    function log(/* arguments */) {
        print.apply(this, ["out"].concat(Array.prototype.slice.call(arguments)));
    }

    log.help = [
        "log(value [, value]*)",
        "prints specified values to the standard output stream"
    ];

    function err(/* arguments */) {
        print.apply(this, ["err"].concat(Array.prototype.slice.call(arguments)));
    }

    log.err = [
        "err(value [, value]*)",
        "prints specified values to the standard error stream"
    ];

    return {
        log: log,
        err: err
    };
});
//
// module
//
var module = feature.create(function () {
    function load(/* arguments */) {
        for (var index = 0; index < arguments.length; index++) {
            var name = arguments[index];
            if (global[name]) {
                // TODO: fix generic error handling.
                throw new Error(name + " is defined as a " + global[name]._type_);
            }
            global[name] = Module_load(name);
        }
    }

    load.help = [
        "load(sub-module [, sub-module]*)",
        "loads and evaluates given sub-modules specified by their name",
        "the name is basically the sub-folder name of the sub-module"
    ];

    function list() {
        return getIdsByType("module");
    }

    list.help = [
        "list()",
        "returns a fileList of loaded sub-module objects"
    ];

    return {
        load: load,
        list: list
    };
});
//
// script
//
var script = feature.create(function () {
    var loaded = [];

    function load(/* */) {
        for (var index = 0; index < arguments.length; index++) {
            var name = arguments[0];
            if (loaded.indexOf(name) != -1) throw new Error("script already loaded: " + name);
            Script_load(name);
            loaded.push(name);
        }
    }

    load.help = [
        "load(file [, file]*)",
        "loads and evaluates specified script(s)"
    ];

    function list() {
        return [].concat(loaded);
    }

    list.help = [
        "list()",
        "returns an array of loaded scripts"
    ];

    return {
        load: load,
        list: list
    }
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// task
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var task = feature.create(function () {
    var tasks = {};

    function lookupName(task) {
        for (var id in global) {
            if (global.hasOwnProperty(id) && global[id] == task) {
                return id;
            }
        }
        return "(n/a)";
    }

    function create(closure) {
        var result = function () {
            if (!result.executed) {
                var index;
                var taskName = lookupName(result);
                var save = global.currentTask;
                global.currentTask = taskName;
                console.log("[", "entering", "]");
                var timestamp = new Date().getTime();
                // call child modules
                var moduleNames = getIdsByType("module");
                for (index = 0; index < moduleNames.length; index++) {
                    var module = global[moduleNames[index]];
                    if (module == global) continue;
                    if (module[taskName] && module[taskName]._type_ == "task") {
                        module[taskName]();
                    }
                }
                // call closure(s)
                for (index = 0; index < result._closures_.length; index++) {
                    result._closures_[index]();
                }
                result.time = new Date().getTime() - timestamp;
                result.executed = true;
                console.log("[", "complete in", result.time, "ms.", "]");
                global.currentTask = save;
            }
            return result.value;
        };
        result._type_ = "task";
        result._closures_ = [closure];
        return result;
    }

    create.help = [
        "create(closure)",
        "creates a task over the specific closure"
    ];

    function append(task, closure) {
        if (!(task._type_ && task._type_ == "task")) throw new Error("not a task");
        task._closures_.push(closure);
    }

    append.help = [
        "append(task, closure)",
        "appends a closure to the specified task"
    ];

    function list() {
        return getIdsByType("task");
    }

    append.help = [
        "list()",
        "returns a fileList of defined tasks"
    ];

    return {
        create: create,
        append: append,
        list: list
    }
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// now load the build.js
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
script.load("build2.js");
